<?php

// PATHS
// -----------------------------------------------------------------

// Root path of your site
$root = $_SERVER['DOCUMENT_ROOT'] . '/';

// Path to downloadable files (will not be revealed to users so they will never know your files real address)
$hidden = 'free-downloads/';

// Path to data files
// NOTE: This directory must have been CHMOD'd to 777
$data = 'free-download-logs/';

// Path to and name of your leech warning page
$leech = 'leech.php';

// Path to and name of your hack warning page
$hack = 'hack.php';

// Allow null referer's to download files (true/false).  If true, and the referer value is null, the user will still be allowed to download.  If false, it will be regarded as a leech attempt.  Default is false.
$allow_null_referer = false;

// Limits the download speed (in KB's per second).  Default is 24 KB's per second.
$speed = 64;


  /*****************************************************************
   **                                                             **
   **                      F U N C T I O N S                      **
   **                                                             **
   *****************************************************************/


// PURPOSE:	Check whether a file exists.  If it doesn't, the file is created.
// PARAMS:	[string] $file - The full path and name of a file
// RETURN:	[bool] - True if file exists | False if file doesn't exist but gets created
function download_check_file($file)
{
	if (!file_exists($file))
	{
		$handle = fopen($file, "w");
		fclose($handle);
		chmod($file, 0666);

		return false;
	}

	return true;
}


// PURPOSE:	To return the number of times a file was downloaded.
// PARAMS:	[string] $file - The name of a file, [string] $category - The category in which the file is located
// RETURN:	[echo] Number of times the file was downloaded
function download_count($file, $category='')
{
	global $root, $hidden, $file_download;
	$category = (!empty($category)) ? $category . '/' : '';
	$count = 0;

	if (file_exists($root . $hidden . $category . $file))
	{
		if (file_exists($file_download))
		{
			// Read data from download file into an array
			$download = explode("\n", file_get_contents($file_download));

			// Loop through the downloadarray
			foreach ($download as $line)
			{
				// Split and store array values
				list($stored_file, $stored_ip, $stored_time, $stored_day, $stored_month, $stored_year) = explode("|", $line);

				// Check to see if the file is under a category
				if (substr_count($stored_file, '/') != 0)
				{
					$array = explode('/', $stored_file);
					$stored_category = $array[0] .'/';
					$stored_file = $array[1];

					// Count how many times the file was downloaded
					if ($category == $stored_category && $file == $stored_file)
					{
						$count++;
					}
				}
				else
				{
					// Count how many times the file was downloaded
					if ($category == '' && $file == $stored_file)
					{
						$count++;
					}
				}
			}
		}
	}

	return $count;
}


// PURPOSE:	To return the size of a file in a user friendly format.
// PARAMS:	[string] $file - The name of a file, [string] $category - The category in which the file is located
// RETURN:	[echo] - Size of file
function download_size($file, $category='')
{
    global $root, $hidden;
	$category = (!empty($category)) ? $category . '/' : '';
	$file = $root . $hidden . $category . $file;
	$decimals = 2;

	if (file_exists($file))
	{
		$size = filesize($file);

		$units = array("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB");
		$units_count = count($units);

		for($unit = 0; $unit < $units_count && $size >= 1024; $unit++)
		{
			$size /= 1024;
		}

		// Don't include decimals if size is B or KB
		$decimals = $unit < 2 ? 0 : $decimals;

		return number_format($size, $decimals) . " " . $units[$unit];
	}
	else
	{
		return "0 B";
	}
}


// PURPOSE:	Check if the text is escaping to another directory
// PARAMS:	[string] $text - The text for the file or category name, retrieved from the $_GET query
// RETURN:	[bool] - True if there is a hack attempt | False if it is clean
function download_hack_attempt_check($text)
{
	if (substr($text, 0, 1) == '.' || strpos($text, '..') > 0 || substr($text, 0, 1) == '/' || strpos($text, '/') > 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}




  /*****************************************************************
   **                                                             **
   **           I N I T I A L I S E   V A R I A B L E S           **
   **                                                             **
   *****************************************************************/

// DATA FILES
$file_download = $root . $data . 'download.log';
$file_leech    = $root . $data . 'leech.log';
$file_hack     = $root . $data . 'hack.log';

// VARIABLES

$hack_attempt = false;

if (!empty($_GET['file']))
{
	$file = str_replace('%20', ' ', $_GET['file']);

	// Check for hack attempt on file
	$hack_attempt = download_hack_attempt_check($file);
}
else
{
	$file = str_replace('%20', ' ', $_SERVER['QUERY_STRING']);

	// Check for hack attempt on file
	$hack_attempt = download_hack_attempt_check($file);
}

if (!empty($_GET['category']))
{
	$category = $_GET['category'];

	// Check for hack attempt on category
	$hack_attempt = download_hack_attempt_check($category);	

	$category = (!$hack_attempt) ? $category . '/' : '';
}
else
{
	$category = '';
}


$file_real     = $root . $hidden . $category . $file;
$ip            = $_SERVER['REMOTE_ADDR'];

// Convert download speed to bytes
$speed = round($speed * 1024);

// Determine domain name of your site
$host          = (substr($_SERVER['HTTP_HOST'], 0, 4) == 'www.') ? substr($_SERVER['HTTP_HOST'], 4, strlen($_SERVER['HTTP_HOST'])) : $_SERVER['HTTP_HOST'];

// Determine domain name of referer site
preg_match("/^(http:\/\/)?([^\/]+)/i", $_SERVER['HTTP_REFERER'], $matches);
$referer       = (substr($matches[2], 0, 4) == 'www.') ? substr($matches[2], 4, strlen($matches[2])) : $matches[2];




  /*****************************************************************
   **                                                             **
   **                         S C R I P T                         **
   **                                                             **
   *****************************************************************/


// Check to see if the download script was called
if (basename($_SERVER['PHP_SELF']) == 'download.php')
{
	if ($_SERVER['QUERY_STRING'] != null)
	{
		// Create log files if they don't exist
		download_check_file($file_download);
		download_check_file($file_leech);
		download_check_file($file_hack);

		// HACK ATTEMPT CHECK
		// Log if an attempt was found
		if ($hack_attempt)
		{
			if ($referer == null)
			{
				$referer = "none";
			}
			$handle_hack = fopen($file_hack, "a");
			flock($handle_hack, LOCK_EX);
			fwrite($handle_hack, date("D y:m:d H:i:s", time()) . " - $ip - $referer - $category$file\n");
			fclose($handle_hack);

			// Hack attempt made so redirect to hack warning page (if found)
			if (file_exists($root . $hack))
			{
				header('Location: http://' . $host . '/' . $hack);
			}
			// Otherwise cause a 403 error (Forbidden)
			else
			{
				header('Location: http://' . $host . '/403');
			}

			die();
		}

		// LEECH ATTEMPT CHECK
		// If the domain name of the referer does not match the
		// the domain name of your site, then someone has tried
		// to leech one of your files!
		if ($referer != null && $referer != $host || $referer == null && !$allow_null_referer)
		{
			if ($referer == null)
			{
				$referer = "none";
			}
			$handle_leech = fopen($file_leech, "a");
			flock($handle_leech, LOCK_EX);
			fwrite($handle_leech, date("D y:m:d H:i:s", time()) . " - $ip - $referer - $category$file\n");
			fclose($handle_leech);

			// Leech attempt made so redirect to leech warning page (if found)
			if (file_exists($root . $leech))
			{
				header('Location: http://' . $host . '/' . $leech);
			}
			// Otherwise cause a 403 error (Forbidden)
			else
			{
				header('Location: http://' . $host . '/403');
			}

			die();
		}

		// If requested file exists
		if (file_exists($file_real))
		{
			// Get extension of requested file
			$extension = strtolower(substr(strrchr($file, "."), 1));

			// Determine correct MIME type
			switch($extension)
			{
				case "bmp":     $type = "image/bmp";                     break;
				case "css":     $type = "text/css";                      break;
				case "doc":     $type = "application/msword";            break;
				case "exe":     $type = "application/octet-stream";      break;
				case "gif":     $type = "image/gif";                     break;
				case "gz":      $type = "application/gzip";              break;
				case "htm":     $type = "text/html";                     break;
				case "html":    $type = "text/html";                     break;
				case "js":      $type = "application/x-javascript";      break;
				case "jpg":     $type = "image/jpg";                     break;
				case "jpe":     $type = "image/jpg";                     break;
				case "jpeg":    $type = "image/jpg";                     break;
				case "pdf":     $type = "application/pdf";               break;
				case "php":     $type = "application/x-httpd-php";       break;
				case "png":     $type = "image/png";                     break;
				case "rar":     $type = "encoding/x-compress";           break;
				case "rtf":     $type = "application/rtf";               break;
				case "tar":     $type = "application/x-tar";             break;
				case "tgz":     $type = "application/gzip";              break;
				case "torrent": $type = "application/x-bittorrent";      break;
				case "txt":     $type = "text/plain";                    break;
				case "xls":     $type = "application/xls";               break;
				case "xml":     $type = "application/xml";               break;
				case "7z":      $type = "application/x-compress";        break;
				case "zip":     $type = "application/x-zip-compressed";  break;

				default:        $type = "application/force-download";    break;
			}

			// Fix IE bug [0]
			$header_file = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? preg_replace('/\./', '%2e', $file, substr_count($file, '.') - 1) : $file;

			// Prepare headers
			header("Pragma: public");
			header("Expires: 0");
			header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
			header("Cache-Control: public", false);
			header("Content-Description: File Transfer");
			header("Content-Type: " . $type);
			header("Accept-Ranges: bytes");
			header("Content-Disposition: attachment; filename=\"" . $header_file . "\";");
			header("Content-Transfer-Encoding: binary");
			header("Content-Length: " . filesize($file_real));

			// Send file for download
			if ($stream = fopen($file_real, 'rb'))
			{
				while(!feof($stream) && connection_status() == 0)
				{
					print(fread($stream, $speed));
					flush();
					sleep(1);
				}

				fclose($stream);
			}

			// Only log completed downloads
			if (connection_status() == 0 && !connection_aborted())
			{
				// Log the download
				$handle_download = fopen($file_download, "a");
				flock($handle_download, LOCK_EX);
				fwrite($handle_download, date("D y:m:d H:i:s", time()) . " - $ip - $referer - $category$file\n");
        
        flock($fp, LOCK_UN);
				fclose($handle_download);
			}
		}
		else
		{
			// Requested file does not exist so cause a 404 error (File not found)
			header('Location: http://' . $host . '/404');

			die();
		}
	}
}
?>